/*
 * Copyright 2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.pajamas.starter.integration.spring.jdbc;

import com.zaxxer.hikari.HikariDataSource;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.pajmas.daydream.building.AbstractEzBuilder;

import javax.sql.DataSource;

import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * @author william
 * @since 2024/1/24
 */
public final class H2DataSourceBuilder extends AbstractEzBuilder<DataSource> {
    private final StringBuilder jdbcUrlBuilder;
    private String jdbcUrl;
    private String username;
    private String password;
    private Long connectionTimeoutMs;
    private Long idleTimeoutMs;
    private Integer maxPoolSize;

    private H2DataSourceBuilder() {
        this.jdbcUrlBuilder = new StringBuilder("jdbc:h2:");
    }


    public static H2DataSourceBuilder newBuilder() {
        return new H2DataSourceBuilder();
    }

    public H2DataSourceBuilder jdbcUrl(String jdbcUrl) {
        this.jdbcUrl = jdbcUrl;
        return this;
    }

    public H2DataSourceBuilder username(String username) {
        this.username = username;
        return this;
    }

    public H2DataSourceBuilder password(String password) {
        this.password = password;
        return this;
    }

    public H2DataSourceBuilder connectionTimeoutMs(long connectionTimeoutMs) {
        this.connectionTimeoutMs = connectionTimeoutMs;
        return this;
    }

    public H2DataSourceBuilder idleTimeoutMs(long idleTimeoutMs) {
        this.idleTimeoutMs = idleTimeoutMs;
        return this;
    }

    public H2DataSourceBuilder embeddedMode() {
        this.jdbcUrlBuilder.append("file:");
        return this;
    }

    public H2DataSourceBuilder memoryMode() {
        this.jdbcUrlBuilder.append("mem:");
        return this;
    }

    public H2DataSourceBuilder tcpServerMode() {
        this.jdbcUrlBuilder.append("tcp:");
        return this;
    }

    public H2DataSourceBuilder tlsServerMode() {
        this.jdbcUrlBuilder.append("ssl:");
        return this;
    }

    public H2DataSourceBuilder mixMode() {
        this.jdbcUrlBuilder.append(";").append("AUTO_SERVER=TRUE");
        return this;
    }

    public H2DataSourceBuilder userHome(String path) {
        this.jdbcUrlBuilder.append("~/").append(path);
        return this;
    }

    public H2DataSourceBuilder path(String path) {
        this.jdbcUrlBuilder.append(path);
        return this;
    }

    public H2DataSourceBuilder ignoreUnknownSettings() {
        this.jdbcUrlBuilder.append(";").append("IGNORE_UNKNOWN_SETTINGS=TRUE");
        return this;
    }

    public H2DataSourceBuilder compatibilityMode(String mode) {
        this.jdbcUrlBuilder.append(";").append("MODE=").append(mode);
        return this;
    }

    public H2DataSourceBuilder autoReconnect() {
        this.jdbcUrlBuilder.append(";").append("AUTO_RECONNECT=TRUE");
        return this;
    }

    public H2DataSourceBuilder pageSize(int pageSize) {
        this.jdbcUrlBuilder.append(";").append("PAGE_SIZE=").append(pageSize);
        return this;
    }

    /**
     * @param key   key
     * @param value value
     * @return org.pajamas.starter.integration.spring.jdbc.H2DataSourceBuilder
     * @author william
     * @since 2024/1/26
     */
    public H2DataSourceBuilder feature(String key, String value) {
        this.jdbcUrlBuilder.append(";").append(key).append("=").append(value);
        return this;
    }

    public H2DataSourceBuilder maxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
        return this;
    }

    @Override
    protected DataSource internalBuild() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(org.h2.Driver.class.getName());
        dataSource.setJdbcUrl(StringUtils.firstNonBlank(this.jdbcUrl, this.jdbcUrlBuilder.toString()));
        dataSource.setUsername(this.username);
        dataSource.setPassword(this.password);
        dataSource.setConnectionTimeout(ObjectUtils.firstNonNull(this.connectionTimeoutMs, SECONDS.toMillis(30)));
        dataSource.setIdleTimeout(ObjectUtils.firstNonNull(this.idleTimeoutMs, MINUTES.toMillis(10)));
        dataSource.setMaximumPoolSize(ObjectUtils.firstNonNull(this.maxPoolSize, 10));
        return dataSource;
    }

    public interface Customizer extends AbstractEzBuilder.Customizer<DataSource, H2DataSourceBuilder> {
    }
}
